package com.jiaming.employee.common.controller;


import com.jiaming.employee.common.exception.ApiException;
import com.jiaming.employee.common.response.ResultCodeEnum;
import com.jiaming.employee.common.response.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

/**
 * @author dragon
 * @create 2020-12-18 16:35
 */
@Slf4j
@RestControllerAdvice
public class ExceptionControllerAdvice {
    // 1、请求参数类型错误。例如：要求传入字段类型是整型，但是传入的是字符串
    // 2、请求方法错误。例如：要求是POST请求，但是执行的是GET请求。
    // 3、请求参数必填项错误。例如：Form表单要求必传某个字段，但是没有传入
    // 4、请求内容类型错误。例如：要求传入的内容是JSON类型，但传入是text类型
    // 5、请求内容为空。例如，请求时，没有传入JSON内容
    // 6、请求内容是JSON格式的参数安全校验，即 使用@Valid。如果校验失败，需要的处理的异常
    // 7、业务异常统一处理

    // 业务异常统一处理
    @ExceptionHandler({ApiException.class})
    public ResultVO<String> apiExceptionHandler(ApiException e) {
        log.warn("警告错误: {}", e.getLocalizedMessage());
        if (e.getCode() != 0) {
            return new ResultVO<>(e.getCode(), e.getMsg(), e.getDetail());
        }
        return new ResultVO<>(ResultCodeEnum.BIZ_FAIL, e.getDetail());
    }

    // 请求内容是JSON格式的参数安全校验，即 使用@Valid。如果校验失败，需要的处理的异常
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResultVO<String> methodArgumentNotValidExceptionHandler(
            MethodArgumentNotValidException e) {
        // 从异常对象中拿到 ObjectError 对象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 然后提取错误提示信息进行返回
        String errorMessage = objectError.getDefaultMessage();
        log.warn("警告信息: {}", errorMessage);
        // 返回错误码，并在 data 中存放具体参数校验失败的原因
        return new ResultVO<>(ResultCodeEnum.FAIL_PARAM_VALIDATE, errorMessage);
    }

    // 请求参数必填项错误。例如：Form表单要求必传某个字段，但是没有传入
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public ResultVO<String> missingServletRequestParameterExceptionHandler(
            MissingServletRequestParameterException e) {
        log.warn("警告错误: {}", e.getLocalizedMessage());
        return new ResultVO<>(ResultCodeEnum.FAIL_PARAM_MISSING, e.getLocalizedMessage());
    }

    // 请求方法错误。例如：要求是POST请求，但是执行的是GET请求。
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public ResultVO<String> httpRequestMethodNotSupportedExceptionHanler(
            HttpRequestMethodNotSupportedException e) {
        log.warn("警告错误: {}", e.getLocalizedMessage());
        return new ResultVO<>(ResultCodeEnum.FAIL_REQUEST_METHOD, e.getLocalizedMessage());
    }

    // 请求参数类型错误，例如：要求传入字段类型是整型，但是传入的是字符串
    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    public ResultVO<String> methodArgumentTypeMismatchExceptionHandler(
            MethodArgumentTypeMismatchException e) {
        log.warn("警告错误: {}", e.getLocalizedMessage());
        return new ResultVO<>(ResultCodeEnum.FAIL_PARAM_TYPE, e.getLocalizedMessage());
    }

    // 请求内容类型错误。例如：要求传入的内容是JSON类型，但传入是text类型
    @ExceptionHandler({HttpMediaTypeNotSupportedException.class})
    public ResultVO<String> httpMediaTypeNotSupportedExceptionHanler(
            HttpMediaTypeNotSupportedException e) {
        log.warn("警告错误: {}", e.getLocalizedMessage());
        return new ResultVO<>(ResultCodeEnum.FAIL_REQUEST_TYPE, e.getLocalizedMessage());
    }

    // 请求内容为空。例如，请求时，没有传入JSON内容
    @ExceptionHandler({HttpMessageNotReadableException.class})
    public ResultVO<String> httpMessageNotReadableException(
            HttpMessageNotReadableException e) {
        log.warn("警告错误: {}", e.getLocalizedMessage());
        return new ResultVO<>(ResultCodeEnum.FAIL_REQUEST_CONTENT, e.getLocalizedMessage());
    }

    // 以上的异常处理器不能处理异常，都会被该处理器处理
    @ExceptionHandler
    public ResultVO<String> exceptionHandler(Exception e) {
        e.printStackTrace();
        String message = e.getClass().getName() + "|" + e.getLocalizedMessage();
        log.error("未知错误: {}", message);
        return new ResultVO<>(ResultCodeEnum.FAIL, message);
    }
}
